Skip to content

fix: inert react 19 support#12315

Open
logonoff wants to merge 1 commit into
patternfly:mainfrom
logonoff:12295-drawerpanel
Open

fix: inert react 19 support#12315
logonoff wants to merge 1 commit into
patternfly:mainfrom
logonoff:12295-drawerpanel

Conversation

@logonoff
Copy link
Copy Markdown
Member

@logonoff logonoff commented Apr 2, 2026

What: Closes #12295

It might be possible that just using inert: 'true' also fixes this problem but I have not verified if that works

Additional issues: issue due to https://stackoverflow.com/questions/72720469/error-when-using-inert-attribute-with-typescript

Summary by CodeRabbit

  • Bug Fixes
    • Improved inert/focus handling across drawers, navigation, search, lists, form groups, tree items and table rows to prevent collapsed/hidden regions from receiving focus and improve screen reader behavior.
    • Ensures consistent collapsed/hidden behavior across React versions, reducing unexpected focus shifts and improving keyboard navigation and accessibility.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 2, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c72e37a5-ae05-4fd1-b6ec-8961e23a5215

📥 Commits

Reviewing files that changed from the base of the PR and between adcb841 and d0060bb.

📒 Files selected for processing (10)
  • packages/react-core/src/components/Compass/Compass.tsx
  • packages/react-core/src/components/Drawer/DrawerPanelContent.tsx
  • packages/react-core/src/components/DualListSelector/DualListSelectorTreeItem.tsx
  • packages/react-core/src/components/Form/InternalFormFieldGroup.tsx
  • packages/react-core/src/components/Nav/NavExpandable.tsx
  • packages/react-core/src/components/SearchInput/SearchInput.tsx
  • packages/react-core/src/components/TreeView/TreeViewListItem.tsx
  • packages/react-core/src/helpers/inert.ts
  • packages/react-table/src/components/Table/Tr.tsx
  • packages/react-table/src/components/Table/utils/inert.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/react-table/src/components/Table/Tr.tsx
  • packages/react-core/src/components/TreeView/TreeViewListItem.tsx
  • packages/react-core/src/components/Nav/NavExpandable.tsx
  • packages/react-core/src/components/DualListSelector/DualListSelectorTreeItem.tsx
  • packages/react-core/src/components/Form/InternalFormFieldGroup.tsx
  • packages/react-core/src/helpers/inert.ts
  • packages/react-core/src/components/Compass/Compass.tsx
  • packages/react-core/src/components/Drawer/DrawerPanelContent.tsx

Walkthrough

Replaced empty-string inert assignments with a version-aware IS_INERT constant and added package-specific inert helpers that compute a boolean-compatible value from React's major version.

Changes

IS_INERT migration

Layer / File(s) Summary
Helpers: add IS_INERT (core + table)
packages/react-core/src/helpers/inert.ts, packages/react-table/src/components/Table/utils/inert.ts
Added exported IS_INERT constants that derive a boolean-compatible value from react's major version (true for React 19+, legacy cast for older versions).
Compass updates
packages/react-core/src/components/Compass/Compass.tsx
Replaced collapsed inert literal with IS_INERT for header, start/end sidebars, and footer; added import.
DrawerPanelContent updates
packages/react-core/src/components/Drawer/DrawerPanelContent.tsx
Added IS_INERT import and used it for shouldCollapseSpace conditional inert prop.
DualListSelectorTreeItem updates
packages/react-core/src/components/DualListSelector/DualListSelectorTreeItem.tsx
Imported IS_INERT and use it when cloning children for collapsed state.
InternalFormFieldGroup updates
packages/react-core/src/components/Form/InternalFormFieldGroup.tsx
Imported IS_INERT and applied it to .formFieldGroupBody when collapsed with animations.
NavExpandable updates
packages/react-core/src/components/Nav/NavExpandable.tsx
Imported IS_INERT and set inert: IS_INERT on collapsed section (left hidden logic unchanged).
SearchInput updates
packages/react-core/src/components/SearchInput/SearchInput.tsx
Imported IS_INERT and replaced inert: '' usages in expandable/animated search builders and toggle rendering with IS_INERT.
TreeViewListItem updates
packages/react-core/src/components/TreeView/TreeViewListItem.tsx
Imported IS_INERT and used it when cloning children while collapsed.
Table Tr updates
packages/react-table/src/components/Table/Tr.tsx
Imported IS_INERT and set inert: IS_INERT for hidden rows when animations are enabled.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • kmcfaul
  • nicolethoen
  • mcoker
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix: inert react 19 support' clearly and concisely describes the main change: fixing the inert attribute to work with React 19, which is the primary objective of this pull request.
Linked Issues check ✅ Passed The changes comprehensively address issue #12295 by implementing version-aware inert attribute handling: for React 19+, uses boolean true; for earlier versions, uses empty-string cast to preserve DOM behavior, eliminating the React warning and ensuring correct cross-version compatibility.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing inert attribute warnings across React versions by introducing IS_INERT helpers and updating seven components plus two utility modules to use the version-aware constant.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@patternfly-build
Copy link
Copy Markdown
Collaborator

patternfly-build commented Apr 2, 2026

@logonoff logonoff force-pushed the 12295-drawerpanel branch from a88edd6 to 3c37bfc Compare April 2, 2026 23:29
@logonoff logonoff changed the title fix(DrawerPanelContent): inert react 19 support fix: inert react 19 support Apr 2, 2026
@logonoff
Copy link
Copy Markdown
Member Author

logonoff commented Apr 2, 2026

@coderabbitai review

Check if it's possible and compatible with React 17, 18, and 19 to just use inert: 'true' as a cleaner workaround to the problem of inert not being supported prior to React 19

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd DrawerPanelContent.tsx --exec cat {}

Length of output: 16858


🏁 Script executed:

#!/bin/bash
fd inert --exec cat {}

Length of output: 278


🌐 Web query:

React 19 boolean attribute inert string "true" warning behavior

💡 Result:

In React 19, inert is treated as a real boolean DOM attribute (like disabled). You should pass a boolean, not a string. inert being present means “true”, and absent means “false”. [1][2]

What happens with inert="true" (string)

If you render:

<div inert="true" />

React 19 will print a dev warning like “Received the string true for the boolean attribute inert… Did you mean inert={true}?”, because "true"/"false" strings are a common bug for boolean attributes (the browser treats inert="false" as enabled, since the attribute is still present). Use inert / inert={true} / inert={someBoolean} instead. [3][4]

Special case: inert="" (empty string)

React 19 also warns on inert="" and (importantly) treats it as false: “Received an empty string for a boolean attribute inert. This will treat the attribute as if it were false…”. So the old React 18-era workaround of using "" to mean “true” for inert is no longer correct in React 19. [2]

Correct React 19 usage

<div inert />                 // true
<div inert={true} />          // true
<div inert={isModalOpen} />   // boolean
<div inert={false} />         // false (attribute removed)

Sources:
[1] MDN: HTMLElement.inert (boolean attribute/property) (developer.mozilla.org)
[2] React DOM attribute test snippet showing inert="" warning + treated as false (fossies.org)
[3] Example of React’s “string 'true' for boolean attribute” warning pattern (bootschool.net)
[4] Explanation that "true"/"false" string values for boolean attributes trigger React warnings (deepscan.io)

Citations:


@logonoff Using inert: 'true' (the string "true") is not a viable universal workaround. Here's why it fails across versions:

React version inert: 'true' behavior Result
17 / 18 inert is unrecognized; React passes the string through to the DOM as inert="true" ✅ Functionally works (browser treats any value as present/truthy for boolean attributes)
19 inert is now a first-class boolean attribute ❌ React 19 emits a dev warning: "Received the string true for the boolean attribute inert… Did you mean inert={true}?"

In React 19, inert is treated as a real boolean DOM attribute. Passing the string "true" triggers a React warning about receiving a string for a boolean attribute, similar to how disabled="true" behaves. This is essentially the same class of warning the PR is trying to resolve, just with a different value.

The current implementation in helpers/inert.ts is the correct approach:

// React < 19: empty string '' → attribute presence in DOM → element is inert ✓  
// React >= 19: boolean true  → correct native boolean value → no warning ✓
export const IS_INERT = Number(version.split('.')[0]) >= 19 ? true : ('' as unknown as boolean);
  • In React < 19, inert="" (empty string) was passed through to the DOM as attribute presence, making the element inert. The '' as unknown as boolean cast satisfies TypeScript while preserving that behavior.
  • In React 19, inert="" is treated as false, breaking the old workaround, so the version check correctly switches to true (boolean).

There's no single value that works warning-free across all three React major versions — the version-guarded IS_INERT helper is the right solution.

[review]


🧠 Learnings used
Learnt from: arpanroy41
Repo: patternfly/patternfly-react PR: 12237
File: packages/react-core/src/components/Toolbar/examples/ToolbarFlexGrowAndWidths.tsx:6-6
Timestamp: 2026-02-06T19:58:56.464Z
Learning: In the patternfly-react repository, default imports from 'react' (e.g., `import React from 'react'`) are restricted. React is available globally through the build configuration, so `React.FunctionComponent` and similar React APIs can be used without importing React.

Learnt from: tlabaj
Repo: patternfly/patternfly-react PR: 12288
File: packages/react-core/src/components/Accordion/__tests__/Accordion.test.tsx:144-150
Timestamp: 2026-03-24T14:47:38.734Z
Learning: In PatternFly React, `isPlain` is a public component prop that defaults to `false` by convention, consistent with other PatternFly components. Omitting `isPlain` is treated as equivalent to `isPlain={false}`. Do not suggest changing the default to `undefined` or altering `!isPlain`-based conditional logic, as this would diverge from established PatternFly API patterns and could be a behavioral breaking change.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/react-table/src/components/Table/Tr.tsx`:
- Line 7: The Tr.tsx file imports IS_INERT from a private path; replace that
private import by either (A) importing IS_INERT from react-core's public
entrypoint (e.g., from '@patternfly/react-core' or the public helpers index if
the project exports it) or (B) remove the private dependency and implement the
inert detection inline in the Tr component (define a local IS_INERT constant
using the same feature-detection logic used in react-core), then use that local
symbol wherever IS_INERT is referenced in Tr.tsx.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5dde64ab-c303-412d-a62d-d54fd01ce0ff

📥 Commits

Reviewing files that changed from the base of the PR and between a88edd6 and 3c37bfc.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (9)
  • packages/react-core/src/components/Compass/Compass.tsx
  • packages/react-core/src/components/Drawer/DrawerPanelContent.tsx
  • packages/react-core/src/components/DualListSelector/DualListSelectorTreeItem.tsx
  • packages/react-core/src/components/Form/InternalFormFieldGroup.tsx
  • packages/react-core/src/components/Nav/NavExpandable.tsx
  • packages/react-core/src/components/SearchInput/SearchInput.tsx
  • packages/react-core/src/components/TreeView/TreeViewListItem.tsx
  • packages/react-core/src/helpers/inert.ts
  • packages/react-table/src/components/Table/Tr.tsx

Comment thread packages/react-table/src/components/Table/Tr.tsx Outdated
@logonoff logonoff force-pushed the 12295-drawerpanel branch from 3c37bfc to 71ccccd Compare April 2, 2026 23:41
@logonoff
Copy link
Copy Markdown
Member Author

logonoff commented Apr 27, 2026

@kmcfaul / @nicolethoen please take a look, thanks!

@logonoff logonoff force-pushed the 12295-drawerpanel branch from 71ccccd to adcb841 Compare April 27, 2026 17:05
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/react-core/src/helpers/inert.ts (1)

3-7: Tighten the JSDoc — the empty string isn't treated as truthy by React JSX.

The mechanism is slightly different from what the comment implies. In React 17/18, inert is not a recognized DOM attribute, so React forwards whatever you pass (including "") directly to the DOM, and the DOM treats the presence of the attribute as "inert" regardless of value. React 19 added inert to its known boolean‑attribute list, so an empty string is now coerced to false (attribute removed) — hence the need to pass boolean true. Recommend updating the doc and adding a short note that the boolean type is a deliberate widening so consumers can spread it as a JSX prop without ceremony.

📝 Suggested doc tweak
 /**
- * Before React 19, React JSX treats empty string "" as truthy for inert prop.
+ * Cross-version value for the `inert` DOM attribute.
+ *
+ * - React >= 19 recognizes `inert` as a boolean attribute and requires `true`
+ *   (an empty string would be coerced to `false` and the attribute removed).
+ * - React < 19 does not recognize `inert`, so an empty string is forwarded
+ *   to the DOM as `inert=""`; the DOM then treats the attribute's presence
+ *   as inert regardless of value.
+ *
+ * The runtime value is widened to `boolean` so consumers can spread it as a
+ * JSX prop without dealing with a `boolean | ''` union.
  * `@see` {`@link` https://stackoverflow.com/questions/72720469}
  */
 export const IS_INERT = Number(version.split('.')[0]) >= 19 ? true : ('' as unknown as boolean);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-core/src/helpers/inert.ts` around lines 3 - 7, The JSDoc for
IS_INERT is misleading about how empty string is treated; update the comment
above the exported constant IS_INERT to explain that in React 17/18 `inert` is
unknown so React forwards attributes verbatim and the DOM treats presence of the
attribute as truthy regardless of value, whereas React 19 treats `inert` as a
known boolean attribute so empty string is coerced to false (removed), and also
add a short note that the type widening to boolean (the cast) is deliberate so
consumers can spread IS_INERT as a JSX prop without needing to convert types;
reference the IS_INERT constant and the version variable in the comment so
future readers know why the runtime check exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/react-core/src/helpers/inert.ts`:
- Around line 1-7: The current IS_INERT computation reads React's version string
naively and will mis-handle prerelease tags like "0.0.0-experimental-...";
update the export const IS_INERT logic to robustly extract the numeric major
version from the version string (e.g. use a regex to find the first contiguous
digits /(\d+)/, parseInt that result, and fall back to 0 when no numeric major
is found) and then compute Number(major) >= 19; change the symbol export const
IS_INERT accordingly so prerelease formats safely fall back to the conservative
behavior.

---

Nitpick comments:
In `@packages/react-core/src/helpers/inert.ts`:
- Around line 3-7: The JSDoc for IS_INERT is misleading about how empty string
is treated; update the comment above the exported constant IS_INERT to explain
that in React 17/18 `inert` is unknown so React forwards attributes verbatim and
the DOM treats presence of the attribute as truthy regardless of value, whereas
React 19 treats `inert` as a known boolean attribute so empty string is coerced
to false (removed), and also add a short note that the type widening to boolean
(the cast) is deliberate so consumers can spread IS_INERT as a JSX prop without
needing to convert types; reference the IS_INERT constant and the version
variable in the comment so future readers know why the runtime check exists.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 755418a0-9e4c-4f6c-8a98-09dd2604fa26

📥 Commits

Reviewing files that changed from the base of the PR and between 71ccccd and adcb841.

📒 Files selected for processing (10)
  • packages/react-core/src/components/Compass/Compass.tsx
  • packages/react-core/src/components/Drawer/DrawerPanelContent.tsx
  • packages/react-core/src/components/DualListSelector/DualListSelectorTreeItem.tsx
  • packages/react-core/src/components/Form/InternalFormFieldGroup.tsx
  • packages/react-core/src/components/Nav/NavExpandable.tsx
  • packages/react-core/src/components/SearchInput/SearchInput.tsx
  • packages/react-core/src/components/TreeView/TreeViewListItem.tsx
  • packages/react-core/src/helpers/inert.ts
  • packages/react-table/src/components/Table/Tr.tsx
  • packages/react-table/src/components/Table/utils/inert.ts
✅ Files skipped from review due to trivial changes (4)
  • packages/react-core/src/components/Drawer/DrawerPanelContent.tsx
  • packages/react-core/src/components/TreeView/TreeViewListItem.tsx
  • packages/react-core/src/components/Nav/NavExpandable.tsx
  • packages/react-core/src/components/Form/InternalFormFieldGroup.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react-table/src/components/Table/utils/inert.ts
  • packages/react-core/src/components/SearchInput/SearchInput.tsx

Comment on lines +1 to +7
import { version } from 'react';

/**
* Before React 19, React JSX treats empty string "" as truthy for inert prop.
* @see {@link https://stackoverflow.com/questions/72720469}
*/
export const IS_INERT = Number(version.split('.')[0]) >= 19 ? true : ('' as unknown as boolean);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Is versiona stable named export from thereact package across React 17, 18, and 19, and what format does it follow?

💡 Result:

Yes. version is a stable named export from the react package across React 17, 18, and 19. Format: it follows the same “version string” format React uses for stable vs prerelease npm channels. For the Latest (stable) channel it’s SemVer-style major.minor.patch (e.g. 18.0.0), but for prerelease channel builds (Canary/Experimental) React generates a version string that includes extra metadata (hash + commit date), in the pattern shown by React’s versioning policy docs (e.g. 18.3.0-canary-- / 0.0.0-experimental--).

Citations:


React's version export is stable, but prerelease builds require special handling.

The version export is confirmed as a stable named export across React 17, 18, and 19. For stable releases, it follows SemVer format (major.minor.patch), so version.split('.')[0] reliably extracts the major version. However, prerelease builds (Canary/Experimental) use an extended format like 0.0.0-experimental-<hash>-<YYYYMMDD>, which would cause the version parsing logic to extract 0 instead of the actual major version. Consider adding a safeguard if prerelease versions need to be handled explicitly, or document that this code path uses the conservative fallback for prerelease builds.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-core/src/helpers/inert.ts` around lines 1 - 7, The current
IS_INERT computation reads React's version string naively and will mis-handle
prerelease tags like "0.0.0-experimental-..."; update the export const IS_INERT
logic to robustly extract the numeric major version from the version string
(e.g. use a regex to find the first contiguous digits /(\d+)/, parseInt that
result, and fall back to 0 when no numeric major is found) and then compute
Number(major) >= 19; change the symbol export const IS_INERT accordingly so
prerelease formats safely fall back to the conservative behavior.

@logonoff logonoff force-pushed the 12295-drawerpanel branch from adcb841 to d0060bb Compare May 13, 2026 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug - [DrawerPanelContent] - Received an empty string for a boolean attribute inert

2 participants